package sisicotren.model.simulator;

import java.util.List;
import java.util.LinkedList;
import java.util.Observable;
import java.util.Iterator;
import java.util.Observer;
import sisicotren.model.simulator.Client;
import sisicotren.model.simulator.Channel;
import sisicotren.model.simulator.ChannelState;
import sisicotren.model.simulator.ClientChannelDispatcher;

public class System extends Observable implements Observer {

	public System(List<Queue<Client>> queues, List<Channel> channels) {
		this.channels = channels;
		this.queues = queues;
		totalClientsInjected = 0;
		// Genero los threads que simulan la atencion de los canales.
		// Es decir, el despacho de los clientes por parte del canal.
		attThreads = new LinkedList<ClientChannelDispatcher>();
		Iterator<Channel> it = this.channels.iterator();
		while(it.hasNext()) {
			Channel ch = it.next();
			ch.addObserver(this);
			ClientChannelDispatcher chDispatcher = new ClientChannelDispatcher(ch);
			attThreads.add(chDispatcher);
			new Thread(chDispatcher).start();
		}
	}

	@Override
	public void update(Observable o, Object arg) {
		// un canal notifica que esta libre.
		Channel ch = (Channel)o;
		// se desocupo el canal, si hay gente en la cola
		// sera atendida.
		if(ch.getCurrentState() == ChannelState.IDLE) {
			if(getQueueOfChannel(ch).size() > 0)
				ch.inject(getQueueOfChannel(ch).poll());
		}
	}
	
	public void inject(Client aClient) {
		Channel targetCh = getChannel();
		if(targetCh != null) {
			targetCh.inject(aClient);
		}
		else {
			getMinQueue().push(aClient);
		}
		
		totalClientsInjected++;
		this.setChanged();
		this.notifyObservers();
	}
	
	public Integer getTotalClientsInjected() {
		return totalClientsInjected;
	}
	
	private Channel getChannel() {
		Iterator<Channel> it = channels.iterator();
		Channel target = null;
		while(it.hasNext()) {
			Channel curr = it.next();
			if(curr.getCurrentState() == ChannelState.IDLE) {
				target = curr;
				break;
			}
		}
		
		return target;
	}
	
	private Queue<Client> getQueueOfChannel(Channel ch) {
		Iterator<Channel> it = channels.iterator();
		int pos = 0;
		while(it.hasNext()) {
			// comparo referencias
			if(it.next() == ch) {
				break;
			}
			pos++;
		}
		if(queues.size() > pos)
			return queues.get(pos);
		
		return null;
	}
	
	private Queue<Client> getMinQueue() {
		// de entre todas las colas, la mas vacia.
		Iterator<Queue<Client>> it = queues.iterator();
		Queue<Client> target = null;
		while(it.hasNext()) {
			if(target == null)
				target = it.next();
			else {
				Queue<Client> curr = it.next();
				if(curr.size() < target.size()) {
					target = curr;
				}
			}
		}
		return target;
	}
	
	public List<Queue<Client>> getQueues() {
		return queues;
	}

	public List<Channel> getChannels() {
		return channels;
	}

	public List<ClientChannelDispatcher> getChannelsDispatchers() {
		return attThreads;
	}
	
	public void stopDispatchers() {
		Iterator<ClientChannelDispatcher> it = attThreads.iterator();
		while(it.hasNext()) {
			it.next().stop();
		}
	}
	
	public void pauseDispatchers() {
		Iterator<ClientChannelDispatcher> it = attThreads.iterator();
		while(it.hasNext()) {
			it.next().pause();
		}
	}
	
	public void resumeDispatchers() {
		Iterator<ClientChannelDispatcher> it = attThreads.iterator();
		while(it.hasNext()) {
			it.next().resume();
		}
	}
	
	private List<Queue<Client>> queues;
	private int totalClientsInjected;
	private List<Channel> channels;
	private List<ClientChannelDispatcher> attThreads;

}
